Agent::Agent()
    : _ctx( nullptr )
    , _sock( nullptr ) {}

Agent()::~Agent() {
    zmq_close( _sock );
    zmq_ctx_destroy( _ctx );
}

int Agent::create( const char* router_, const char* id_, agent_cb cb_ ) {
    const char* addr = router_;

    if ( ( _ctx = zmq_ctx_new() ) == nullptr ) {
        return -1;
    }

    if ( ( _sock = zmq_socket( pCtx, ZMQ_DEALER ) ) == nullptr ) {
        zmq_ctx_destroy( _ctx );
        return -1;
    }

    int snd_timeout = 5000;  // millsecond
    if ( zmq_setsockopt( pSock, ZMQ_RCVTIMEO, &iSndTimeout, sizeof( snd_timeout ) ) < 0 )
        goto err_exit;

    if ( zmq_setsockopt( _sock, ZMQ_IDENTITY, id_, strlen( id_ ) ) < 0 )
        goto err_exit;

    if ( zmq_connect( pSock, addr ) < 0 )
        goto err_exit;

    LOG( "connect to [%s]\n", addr );
    ZmqSock zmqsock;
    zmqsock.iType = ZMQ_DEALER;
    zmqsock.sock  = pSock;

    return 0;

err_exit:
    zmq_close( pSock );
    zmq_ctx_destroy( pCtx );
    return -1;
}

int Agent::recv( ZmqSock* zmqsock, Zmqmsg* zMsg ) {
    if ( NULL == zmqsock || NULL == zMsg ) {
        return -1;
    }

    int   len  = sizeof( iType );
    void* sock = zmqsock->sock;
    do {
        switch ( zmqsock->iType ) {
        default:
            LOG( "unknown sock : []\n" );
            return 0;
        case ZMQ_ROUTER:
            LOG( "router:\n" );
            errno = 0;
            if ( zmq_recv( sock, zMsg->szSrc, sizeof( zMsg->szSrc ), 0 ) < 0 ) {
                LOG( "send msg faild : [%s]\n", zmq_strerror( errno ) );
                break;
            }

            LOG( "recv : [%s]\n", zMsg->szSrc );
            if ( zmq_recv( sock, NULL, 0, 0 ) < 0 ) {
                LOG( "send msg faild : [%s]\n", zmq_strerror( errno ) );
                break;
            }
            LOG( "recv : []\n" );
            break;

        case ZMQ_DEALER:
            LOG( "dealer:\n" );
            if ( zmq_recv( sock, NULL, 0, 0 ) < 0 ) {
                LOG( "send msg faild : [%s]\n", zmq_strerror( errno ) );
                break;
            }
            LOG( "recv : []\n" );
            break;

        case ZMQ_REQ:
            LOG( "req:\n" );
            break;

        case ZMQ_REP:
            LOG( "rep:\n" );
            break;
        }

        if ( zmq_recv( sock, zMsg->szDst, sizeof( zMsg->szDst ), 0 ) < 0 ) {
            LOG( "send msg faild : [%s]\n", zmq_strerror( errno ) );
            break;
        }

        LOG( "recv : [%s]\n", zMsg->szDst );
        if ( zmq_recv( sock, NULL, 0, 0 ) < 0 ) {
            LOG( "send msg faild : [%s]\n", zmq_strerror( errno ) );
            break;
        }
        LOG( "recv : []\n" );
        if ( zmq_recv( sock, zMsg->szMsg, sizeof( zMsg->szMsg ), 0 ) < 0 ) {
            LOG( "send msg faild : [%s]\n", zmq_strerror( errno ) );
            break;
        }

        LOG( "recv : [%s]\n", zMsg->szMsg );
        iRet = 0;
    } while ( 0 );

    return iRet;
}

int Agent::send( ZmqSock* zmqsock, Zmqmsg* zMsg ) {
    if ( NULL == zmqsock || NULL == zMsg ) {
        return -1;
    }

    void* sock = zmqsock->sock;
    do {
        switch ( zmqsock->type ) {
        default:
            LOG( "unknown sock : []\n" );
            return 0;

        case ZMQ_ROUTER:
            LOG( "router:\n" );
            if ( zmq_send( sock, zMsg->szDst, strlen( zMsg->szDst ), ZMQ_SNDMORE ) < 0 ) {
                LOG( "send msg faild : [%s]\n", zmq_strerror( errno ) );
                break;
            }

            LOG( "send : [%s]\n", zMsg->szDst );
            if ( zmq_send( sock, "", 0, ZMQ_SNDMORE ) < 0 ) {
                LOG( "send msg faild : [%s]\n", zmq_strerror( errno ) );
                break;
            }

            LOG( "send : []\n" );
            if ( zmq_send( sock, zMsg->szSrc, strlen( zMsg->szSrc ), ZMQ_SNDMORE ) < 0 ) {
                LOG( "send msg faild : [%s]\n", zmq_strerror( errno ) );
                break;
            }

            LOG( "send : [%s]\n", zMsg->szSrc );
            break;

        case ZMQ_DEALER:
            LOG( "dealer:\n" );
            if ( zmq_send( sock, "", 0, ZMQ_SNDMORE ) < 0 ) {
                LOG( "send msg faild : [%s]\n", zmq_strerror( errno ) );
                break;
            }

            LOG( "send : []\n" );
            if ( zmq_send( sock, zMsg->szDst, strlen( zMsg->szDst ), ZMQ_SNDMORE ) < 0 ) {
                LOG( "send msg faild : [%s]\n", zmq_strerror( errno ) );
                break;
            }
            LOG( "send : [%s]\n", zMsg->szDst );
            break;

        case ZMQ_REQ:
            [[fallthrough]];
        case ZMQ_REP:
            LOG( "rex:\n" );
            if ( zmq_send( sock, zMsg->szDst, strlen( zMsg->szDst ), ZMQ_SNDMORE ) < 0 ) {
                LOG( "send msg faild : [%s]\n", zmq_strerror( errno ) );
                break;
            }
            LOG( "send : [%s]\n", zMsg->szDst );
            break;
        }

        if ( zmq_send( sock, "", 0, ZMQ_SNDMORE ) < 0 ) {
            LOG( "send msg faild : [%s]\n", zmq_strerror( errno ) );
            break;
        }
        LOG( "send : []\n" );

        if ( zmq_send( sock, zMsg->szMsg, strlen( zMsg->szMsg ), 0 ) < 0 ) {
            LOG( "send msg faild : [%s]\n", zmq_strerror( errno ) );
            break;
        }
        LOG( "send : [%s]\n", zMsg->szMsg );
        iRet = 0;
    } while ( 0 );

    return iRet;
}